//
//  AudioOutputUnit.m
//  MantuPlay
//
//  Created by Markus Sintonen on 7.11.2009.
//  Copyright 2009 __MyCompanyName__. All rights reserved.
//

#import "AudioOutputUnit.h"
#import "AudioEngineHelpers.h"

@implementation AudioOutputUnit

@synthesize audioFormat, delegate;

-(id)initWithDelegate:(id<AudioOutputUnitDelegate>)del {
	if ((self = [super init]) != nil) {
		self.delegate = del;
		[self setupAudioOutputUnit];
	}
	return self;
}

static OSStatus audioOutputCallback(void *inRefCon, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, 
								 UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData)
{
	if(inBusNumber != 0) return noErr;
	
	if(ioData->mNumberBuffers != 1) {
		NSLog(@"Unexpected condition: mNumberBuffers != 1");
		exit(-1);
	}
	
	AudioOutputUnit *audioOutputUnit = (AudioOutputUnit*)inRefCon;
	id<AudioOutputUnitDelegate> delegate = audioOutputUnit.delegate;
	
	SInt32 *frameBuffer = ((SInt32*)ioData->mBuffers[0].mData);
	[delegate audioOutputUnit:audioOutputUnit fillFrameBuffer:frameBuffer withNumberOfFrames:inNumberFrames];
	
    return noErr;
}

-(void)setupAudioOutputUnit {
	// Describe audio component
	AudioComponentDescription desc;
	desc.componentType = kAudioUnitType_Output;
	desc.componentSubType = kAudioUnitSubType_RemoteIO;
	desc.componentFlags = 0;
	desc.componentFlagsMask = 0;
	desc.componentManufacturer = kAudioUnitManufacturer_Apple;
	
	// Get component
	AudioComponent inputComponent = AudioComponentFindNext(NULL, &desc);
	
	// Get audio units
	OSStatus status = AudioComponentInstanceNew(inputComponent, &audioUnit);
	ERRCHECK(status);
	
	UInt32 flag = 1;
	// Enable IO for playback
	status = AudioUnitSetProperty(audioUnit, 
								  kAudioOutputUnitProperty_EnableIO, 
								  kAudioUnitScope_Output, 
								  0,
								  &flag, 
								  sizeof(flag));
	ERRCHECK(status);
	
	// Enable IO for playback
	status = AudioUnitSetProperty(audioUnit, 
								  kAudioOutputUnitProperty_StartTimestampsAtZero, 
								  kAudioUnitScope_Output, 
								  0,
								  &flag, 
								  sizeof(flag));
	ERRCHECK(status);
	
	// Describe format
	audioFormat.mSampleRate			= 44100.00;
	audioFormat.mFormatID			= kAudioFormatLinearPCM;
	audioFormat.mFormatFlags		= kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked | kAudioFormatFlagsNativeEndian;
	audioFormat.mFramesPerPacket	= 1;
	audioFormat.mChannelsPerFrame	= 2;
	audioFormat.mBitsPerChannel		= 16;
	audioFormat.mBytesPerPacket		= 4;
	audioFormat.mBytesPerFrame		= 4;
	
	//Apply format
	status = AudioUnitSetProperty(audioUnit, 
								  kAudioUnitProperty_StreamFormat, 
								  kAudioUnitScope_Input, 
								  0, 
								  &audioFormat, 
								  sizeof(audioFormat));
	ERRCHECK(status);
	
	// Set up the playback  callback
	AURenderCallbackStruct callbackStruct;
	callbackStruct.inputProc = audioOutputCallback;
	callbackStruct.inputProcRefCon = self;
	
	status = AudioUnitSetProperty(audioUnit, 
								  kAudioUnitProperty_SetRenderCallback, 
								  kAudioUnitScope_Global, 
								  0,
								  &callbackStruct, 
								  sizeof(callbackStruct));
	ERRCHECK(status);
	
	// Initialise
	status = AudioUnitInitialize(audioUnit);
	ERRCHECK(status);
}

-(void)start {
	OSStatus result = AudioOutputUnitStart(audioUnit);
	ERRCHECK(result);
}
-(void)stop {
	OSStatus result = AudioOutputUnitStop(audioUnit);
	ERRCHECK(result);
}

-(void)unloadAudioOutputUnit {
	OSStatus status = AudioComponentInstanceDispose(audioUnit);
	ERRCHECK(status);
}

- (void)dealloc {
	[self unloadAudioOutputUnit];
    [super dealloc];
}

@end
